#define _POSIX_C_SOURCE 200809L
#include "ipc.h"
#include <errno.h>
#include <fcntl.h>
#include <json-c/json.h>
#include <libnotify/notify.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>

#define SORT_CMD "sort_windows"
#define CLEAN_CMD "clean_inactive"
#define AUTO_SPLIT_CMD "auto_split"
#define NOTIFY_TIMEOUT 3000

typedef struct {
  const char *title;
  const char *con_id;
} WindowInfo;

WindowInfo g_window_list[32];
int g_window_count = 0;

void init_notify(void) {
  if (!notify_init("i3 窗口管理器")) {
    fprintf(stderr, "通知服务初始化失败\n");
    exit(EXIT_FAILURE);
  }
}

void send_notify(const char *title, const char *msg, int timeout) {
  NotifyNotification *n = notify_notification_new(title, msg, "dialog-information");
  if (!n) return;
  notify_notification_set_timeout(n, timeout);
  notify_notification_show(n, NULL);
  g_object_unref(G_OBJECT(n));
}

int connect_i3(void) {
  const char *sock_path = getenv("I3SOCK");
  if (!sock_path) {
    fprintf(stderr, "未找到I3SOCK环境变量（i3未启动？）\n");
    return -1;
  }

  int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
  if (sockfd == -1) {
    perror("创建socket失败");
    return -1;
  }

  struct sockaddr_un addr;
  memset(&addr, 0, sizeof(addr));
  addr.sun_family = AF_UNIX;
  strncpy(addr.sun_path, sock_path, sizeof(addr.sun_path) - 1);

  if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
    perror("连接i3失败");
    close(sockfd);
    return -1;
  }

  return sockfd;
}

char *send_i3_cmd(int sockfd, uint32_t cmd_type, const char *payload) {
  i3_ipc_header_t req_hdr;
  memset(&req_hdr, 0, sizeof(req_hdr));
  strncpy(req_hdr.magic, I3_IPC_MAGIC, 6);
  req_hdr.size = (payload) ? strlen(payload) : 0;
  req_hdr.type = cmd_type;

  if (write(sockfd, &req_hdr, sizeof(req_hdr)) != sizeof(req_hdr)) {
    perror("发送请求头失败");
    return NULL;
  }

  if (payload && write(sockfd, payload, req_hdr.size) != (ssize_t)req_hdr.size) {
    perror("发送请求参数失败");
    return NULL;
  }

  i3_ipc_header_t res_hdr;
  ssize_t nread;
  while ((nread = read(sockfd, &res_hdr, sizeof(res_hdr))) != sizeof(res_hdr)) {
    if (nread == -1 && errno != EINTR) {
      perror("读取响应头失败");
      return NULL;
    }
  }

  char *res = malloc(res_hdr.size + 1);
  if (!res) {
    perror("内存分配失败");
    return NULL;
  }
  size_t total_read = 0;
  while (total_read < res_hdr.size) {
    nread = read(sockfd, res + total_read, res_hdr.size - total_read);
    if (nread == -1 && errno != EINTR) {
      perror("读取响应内容失败");
      free(res);
      return NULL;
    }
    total_read += nread;
  }
  res[res_hdr.size] = '\0';

  return res;
}

void subscribe_i3_events(int sockfd) {
  const char *payload = "[\"workspace\", \"window\", \"tick\"]";
  char *res = send_i3_cmd(sockfd, I3_IPC_MESSAGE_TYPE_SUBSCRIBE, payload);
  if (!res) return;

  json_object *root = json_tokener_parse(res);
  json_object *success;
  if (json_object_object_get_ex(root, "success", &success) && json_object_get_boolean(success)) {
    printf("事件订阅成功：已监听工作区、窗口、TICK事件\n");
    send_notify("i3窗口管理器已启动", "指令：sort_windows(排序)、clean_inactive(清理)、auto_split(分屏)", 5000);
  } else {
    fprintf(stderr, "事件订阅失败：%s\n", res);
  }

  json_object_put(root);
  free(res);
}

void collect_window_info(json_object *node) {
  if (!node || g_window_count >= 32) return;

  json_object *type_obj;
  if (json_object_object_get_ex(node, "type", &type_obj)) {
    const char *type = json_object_get_string(type_obj);
    if (strcmp(type, "con") == 0) {
      json_object *window_obj;
      if (json_object_object_get_ex(node, "window", &window_obj)) {
        json_object *name_obj;
        const char *title = "未知窗口";
        if (json_object_object_get_ex(node, "name", &name_obj)) {
          title = json_object_get_string(name_obj);
        } else if (json_object_object_get_ex(node, "window_title", &name_obj)) {
          title = json_object_get_string(name_obj);
        }

        json_object *con_id_obj;
        const char *con_id = NULL;
        if (json_object_object_get_ex(node, "id", &con_id_obj)) {
          con_id = json_object_get_string(con_id_obj);
        }

        if (con_id) {
          g_window_list[g_window_count].title = title;
          g_window_list[g_window_count].con_id = con_id;
          g_window_count++;
        }
      }
    }
  }

  json_object *nodes_obj;
  if (json_object_object_get_ex(node, "nodes", &nodes_obj)) {
    int node_count = json_object_array_length(nodes_obj);
    for (int i = 0; i < node_count; i++) {
      collect_window_info(json_object_array_get_idx(nodes_obj, i));
    }
  }

  json_object *floats_obj;
  if (json_object_object_get_ex(node, "floating_nodes", &floats_obj)) {
    int float_count = json_object_array_length(floats_obj);
    for (int i = 0; i < float_count; i++) {
      collect_window_info(json_object_array_get_idx(floats_obj, i));
    }
  }
}

int get_current_window_list(int sockfd) {
  g_window_count = 0;
  memset(g_window_list, 0, sizeof(g_window_list));

  char *res = send_i3_cmd(sockfd, I3_IPC_MESSAGE_TYPE_GET_TREE, NULL);
  if (!res) return -1;

  json_object *root = json_tokener_parse(res);
  if (!root) {
    fprintf(stderr, "解析i3树失败\n");
    free(res);
    return -1;
  }

  json_object *current_ws = NULL;
  json_object *nodes_obj = json_object_object_get(root, "nodes");
  int root_node_count = json_object_array_length(nodes_obj);

  for (int i = 0; i < root_node_count; i++) {
    json_object *node = json_object_array_get_idx(nodes_obj, i);
    json_object *type_obj = json_object_object_get(node, "type");
    if (!type_obj) continue;

    const char *type = json_object_get_string(type_obj);
    if (strcmp(type, "output") == 0) {
      json_object *output_nodes = json_object_object_get(node, "nodes");
      int output_node_count = json_object_array_length(output_nodes);
      for (int j = 0; j < output_node_count; j++) {
        json_object *ws_node = json_object_array_get_idx(output_nodes, j);
        json_object *ws_type = json_object_object_get(ws_node, "type");
        if (!ws_type || strcmp(json_object_get_string(ws_type), "workspace") != 0) continue;

        json_object *focused_obj = json_object_object_get(ws_node, "focused");
        if (focused_obj && json_object_get_boolean(focused_obj)) {
          current_ws = ws_node;
          break;
        }
      }
      if (current_ws) break;
    }
  }

  if (current_ws) {
    collect_window_info(current_ws);
  }

  json_object_put(root);
  free(res);
  return g_window_count;
}

int compare_window_title(const void *a, const void *b) {
  WindowInfo *win_a = (WindowInfo *)a;
  WindowInfo *win_b = (WindowInfo *)b;
  return strcmp(win_a->title, win_b->title);
}

void sort_windows(int sockfd) {
  int win_count = get_current_window_list(sockfd);
  if (win_count <= 1) {
    send_notify("排序失败", "当前工作区窗口数≤1，无需排序", NOTIFY_TIMEOUT);
    return;
  }

  qsort(g_window_list, win_count, sizeof(WindowInfo), compare_window_title);

  char cmd[128];
  snprintf(cmd, sizeof(cmd), "[con_id=%s] focus", g_window_list[0].con_id);
  send_i3_cmd(sockfd, I3_IPC_MESSAGE_TYPE_RUN_COMMAND, cmd);

  for (int i = 1; i < win_count; i++) {
    snprintf(cmd, sizeof(cmd), "focus right; [con_id=%s] move right, split v", g_window_list[i].con_id);
    send_i3_cmd(sockfd, I3_IPC_MESSAGE_TYPE_RUN_COMMAND, cmd);
  }

  char msg[64];
  snprintf(msg, sizeof(msg), "已按标题排序%d个窗口", win_count);
  send_notify("排序成功", msg, NOTIFY_TIMEOUT);
}

void clean_inactive_windows(int sockfd) {
  char *res = send_i3_cmd(sockfd, I3_IPC_MESSAGE_TYPE_GET_TREE, NULL);
  if (!res) return;

  json_object *root = json_tokener_parse(res);
  const char *focused_con_id = NULL;
  json_object *focused_obj = json_object_object_get(root, "focused");
  if (focused_obj && json_object_get_boolean(focused_obj)) {
    json_object *id_obj = json_object_object_get(root, "id");
    focused_con_id = json_object_get_string(id_obj);
  } else {
    json_object *nodes_obj = json_object_object_get(root, "nodes");
    int node_count = json_object_array_length(nodes_obj);
    for (int i = 0; i < node_count; i++) {
      json_object *node = json_object_array_get_idx(nodes_obj, i);
      json_object *node_focused = json_object_object_get(node, "focused");
      if (node_focused && json_object_get_boolean(node_focused)) {
        json_object *id_obj = json_object_object_get(node, "id");
        focused_con_id = json_object_get_string(id_obj);
        break;
      }
    }
  }

  json_object_put(root);
  free(res);

  if (!focused_con_id) {
    send_notify("清理失败", "未找到当前聚焦窗口", NOTIFY_TIMEOUT);
    return;
  }

  int win_count = get_current_window_list(sockfd);
  if (win_count <= 1) {
    send_notify("清理失败", "当前工作区窗口数≤1，无需清理", NOTIFY_TIMEOUT);
    return;
  }

  int closed_count = 0;
  char cmd[128];
  for (int i = 0; i < win_count; i++) {
    if (strcmp(g_window_list[i].con_id, focused_con_id) != 0) {
      snprintf(cmd, sizeof(cmd), "[con_id=%s] kill", g_window_list[i].con_id);
      send_i3_cmd(sockfd, I3_IPC_MESSAGE_TYPE_RUN_COMMAND, cmd);
      closed_count++;
    }
  }

  char msg[64];
  snprintf(msg, sizeof(msg), "已关闭%d个非活跃窗口", closed_count);
  send_notify("清理成功", msg, NOTIFY_TIMEOUT);
}

void auto_split_windows(int sockfd) {
  int win_count = get_current_window_list(sockfd);
  if (win_count <= 1) {
    send_notify("分屏失败", "当前工作区窗口数≤1，无需分屏", NOTIFY_TIMEOUT);
    return;
  }

  const char *split_mode = (win_count <= 2) ? "h" : "v";
  char cmd[128];

  send_i3_cmd(sockfd, I3_IPC_MESSAGE_TYPE_RUN_COMMAND, "layout stacking");

  snprintf(cmd, sizeof(cmd), "[con_id=%s] focus", g_window_list[0].con_id);
  send_i3_cmd(sockfd, I3_IPC_MESSAGE_TYPE_RUN_COMMAND, cmd);

  for (int i = 1; i < win_count; i++) {
    snprintf(cmd, sizeof(cmd), "focus right; [con_id=%s] move right, split %s", g_window_list[i].con_id, split_mode);
    send_i3_cmd(sockfd, I3_IPC_MESSAGE_TYPE_RUN_COMMAND, cmd);
  }

  char msg[64];
  const char *mode_desc = (win_count <= 2) ? "横向" : "纵向";
  snprintf(msg, sizeof(msg), "已自动%s分屏（%d个窗口）", mode_desc, win_count);
  send_notify("分屏成功", msg, NOTIFY_TIMEOUT);
}

void handle_i3_events(int sockfd) {
  i3_ipc_header_t event_hdr;
  ssize_t nread;

  while (1) {
    while ((nread = read(sockfd, &event_hdr, sizeof(event_hdr))) != sizeof(event_hdr)) {
      if (nread == -1 && errno != EINTR) {
        perror("读取事件头失败");
        return;
      }
    }

    if (event_hdr.type == I3_IPC_EVENT_TICK) {
      char *payload = malloc(event_hdr.size + 1);
      if (!payload) continue;

      size_t total_read = 0;
      while (total_read < event_hdr.size) {
        nread = read(sockfd, payload + total_read, event_hdr.size - total_read);
        if (nread == -1 && errno != EINTR) {
          perror("读取 TICK 事件失败");
          free(payload);
          payload = NULL;
          break;
        }
        total_read += nread;
      }

      if (payload) {
        payload[event_hdr.size] = '\0';
        json_object *root = json_tokener_parse(payload);
        json_object *payload_obj;

        if (json_object_object_get_ex(root, "payload", &payload_obj)) {
          const char *cmd = json_object_get_string(payload_obj);
          if (strcmp(cmd, SORT_CMD) == 0) {
            sort_windows(sockfd);
          } else if (strcmp(cmd, CLEAN_CMD) == 0) {
            clean_inactive_windows(sockfd);
          } else if (strcmp(cmd, AUTO_SPLIT_CMD) == 0) {
            auto_split_windows(sockfd);
          }
        }

        json_object_put(root);
        free(payload);
      }
    } else if (event_hdr.type == I3_IPC_EVENT_WINDOW) {
      auto_split_windows(sockfd);
    }
  }
}

int main(void) {
  printf("===== i3 窗口管理器 =====\n");
  printf("支持功能：\n");
  printf("1. 窗口排序：i3-msg \"nop --ipc-send-tick 'sort_windows'\"\n");
  printf("2. 清理非活跃窗口：i3-msg \"nop --ipc-send-tick 'clean_inactive'\"\n");
  printf("3. 自动分屏：i3-msg \"nop --ipc-send-tick 'auto_split'\"\n");
  printf("退出：关闭终端或按 Ctrl+C\n\n");

  init_notify();

  int sockfd = connect_i3();
  if (sockfd == -1) {
    notify_uninit();
    return EXIT_FAILURE;
  }

  subscribe_i3_events(sockfd);

  handle_i3_events(sockfd);

  close(sockfd);
  notify_uninit();
  return EXIT_SUCCESS;
}
